package cn.wywk.demo.controller;

import cn.wywk.demo.entiti.Analysis;
import cn.wywk.demo.entiti.AnalysisBO;
import cn.wywk.demo.entiti.Util;
import cn.wywk.demo.listener.DemoDataListener;
import cn.wywk.demo.util.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;

/**
 * ========================
 *
 * @author 王国梁
 * @version V1.0
 * @date 2024/04/25 19:18
 * ========================
 */
@RestController
@RequestMapping("/util/")
@Slf4j
public class UtilController {
    private static final Set<String> nameList = new HashSet<>();
    static {
        nameList.add("第三方域名链接");
        nameList.add("落地页链接");
        nameList.add("着陆页链接");
        nameList.add("落地页");
        nameList.add("链接");
    }
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
            // 核心线程数
            10,
            // 最大线程数
            20,
            // 空闲线程最大存活时间
            60L,
            // 空闲线程最大存活时间单位
            TimeUnit.SECONDS,
            // 等待队列及大小
            new ArrayBlockingQueue<>(1000000),
            // 创建新线程时使用的工厂
            Executors.defaultThreadFactory(),
            // 当线程池达到最大时的处理策略
//                new ThreadPoolExecutor.AbortPolicy()          // 抛出RejectedExecutionHandler异常
            new ThreadPoolExecutor.CallerRunsPolicy()       // 交由调用者的线程执行
//                new ThreadPoolExecutor.DiscardOldestPolicy()  // 丢掉最早未处理的任务
//                new ThreadPoolExecutor.DiscardPolicy()        // 丢掉新提交的任务
    );
    @Resource
    private RestTemplate restTemplate;

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒") ;
    /**
     * 文件上传
     * <p>1. 创建excel对应的实体对象
     * <p>3. 直接读即可
     */
    @PostMapping("analysis")
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws IOException {
        DemoDataListener demoDataListener = new DemoDataListener();
        EasyExcel.read(file.getInputStream(), Util.class, demoDataListener).sheet().doRead();
        long s = System.currentTimeMillis();
//        for (Util util : demoDataListener.getCachedDataList()) {
//            System.out.println(util);
            send(demoDataListener.getCachedDataList());
        long e = System.currentTimeMillis();
        log.info("解析耗时:{}秒",(e-s)*1.0/1000);
//        }
        //回写
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(dateTimeFormatter);
        ExcelUtil.export(format+"解析后的数据.xlsx","Sheet1",Util.class,demoDataListener.getCachedDataList(),response);
        return "success";
    }

    @PostMapping("analysisV2")
    @ResponseBody
    public String analysisV2(@RequestParam("file") MultipartFile file, @RequestParam("name") @NonNull String name , HttpServletResponse response) throws IOException {
        if (!StringUtils.isEmpty(name)) {
            for (String s : name.split(",")) {
                nameList.add(s.trim());
            }
        }
        AnalysisBO bo = new AnalysisBO();
        bo.setName(nameList);
        bo.setData(new ArrayList<>());
        bo.setIndex(new ArrayList<>());
        bo.setHitItName(new ArrayList<>());
        EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Map<Integer,String>>() {
            @Override
            public void invoke(Map<Integer,String> o, AnalysisContext analysisContext) {
                bo.getData().add(o);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {

            }

            /**
             * 这里会一行行的返回头
             *
             * @param headMap
             * @param context
             */
            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                log.info("解析到一条头数据:{}", JSONObject.toJSONString(headMap));
                bo.setSize(headMap.size());
                List<String> head = new ArrayList<>();
                bo.setHead(head);
                headMap.forEach((integer, readCellData) -> {
                    String stringValue = readCellData.getStringValue();
                    head.add(stringValue);
                    if (bo.getName().contains(stringValue)) {
                        log.info("命中表头名称为:{}的列是需要解析访问的url", stringValue);
                        bo.getHitItName().add(stringValue);
                        bo.getIndex().add(integer);
                    }
                });
                // 如果想转成成 Map<Integer,String>
                // 方案1： 不要implements ReadListener 而是 extends AnalysisEventListener
                // 方案2： 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换
            }
        }).sheet().doRead();
        long s = System.currentTimeMillis();
//        for (Util util : demoDataListener.getCachedDataList()) {
//            System.out.println(util);
        send(bo);
        long e = System.currentTimeMillis();
        log.info("解析耗时:{}秒",(e-s)*1.0/1000);
//        }
        log.info("本次解析命中的列为:{}",bo.getHitItName());
        //回写
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(dateTimeFormatter);
        List<List<String>> head = new ArrayList<>();
        for (String string : bo.getHead()) {
            ArrayList<String> objects = new ArrayList<>();
            objects.add(string);
            head.add(objects);
        }
        for (String string : bo.getHitItName()) {
            ArrayList<String> objects = new ArrayList<>();
            objects.add("访问状态");
            head.add(objects);
            objects = new ArrayList<>();
            objects.add("是否能够访问");
            head.add(objects);
        }
        //
        List<List<String>> data = new ArrayList<>();
        for (Map<Integer, String> datum : bo.getData()) {
            List<String> objects = new ArrayList<>();
            for (int i = 0; i < datum.size(); i++) {
                objects.add(datum.get(i));
            }
            data.add(objects);
        }
        ExcelUtil.pageExport(format+"解析后的数据.xlsx","Sheet1",head,data,100000,response);
        return "success";
    }

    private void send(List<Util> util) {
        log.info("需要解析的个数{}",util.size());
        int size = util.size();
        int number  = size% 10 == 0 ? size/10 : size/10 +1;
        int start = 0;
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            int s = start;
            int e = s+number >= size ? size:s+number;
            executor.execute(() -> {
                send1(util.subList(s,e));
                countDownLatch.countDown();
            });
            start += number;
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void send(AnalysisBO bo) {
        log.info("需要解析的个数{}",bo.getData().size());
        int size = bo.getData().size();
        int number  = size% 10 == 0 ? size/10 : size/10 +1;
        int start = 0;
        int t = size/number;
        CountDownLatch countDownLatch = new CountDownLatch(t);
        for (int i = 0; i < t; i++) {
            int s = start;
            int e = s+number >= size ? size:s+number;
            if (e > size) {
                countDownLatch.countDown();
                continue;
            }
            executor.execute(() -> {
                send1(bo.getData().subList(s,e),bo.getIndex());
                countDownLatch.countDown();
            });
            start += number;

        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void send1(List<Util> utilList) {
        for (Util util : utilList) {
            send(util,true,true);
        }


    }
    private void send1(List<Map<Integer,String>> utilList,List<Integer> index) {
        for (Map<Integer,String> map : utilList) {
            send(map,index,true,true);
        }


    }

    // a是使用https还是http  b是出现特点异常还是否重试
    private void send(Util util,boolean a,boolean b) {
        String urlString = a ? "https://www."+util.getUrl() : "http://www."+util.getUrl();
        if (StringUtils.isEmpty(util.getUrl())) {
            log.info("落地页链接为空,跳过!");
            return;
        }
        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//            connection.setRequestProperty("User-Agent", "Mozilla/4.0");
//            connection.setRequestProperty("authority", "https://app.upsellingtech.com/");
//            connection.setRequestProperty("method", "OPTIONS");
//            connection.setRequestProperty("path", "/api/customers");
//            connection.setRequestProperty("scheme", "https");
//            connection.setRequestProperty("Accept", "*/*");
//            connection.setRequestProperty("Accept-Encoding", "gzip, deflate, br, zstd");
//            connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9");
//            connection.setRequestProperty("Access-Control-Request-Headers", "content-type");
//            connection.setRequestProperty("Access-Control-Request-Method", "POST");
//            connection.setRequestProperty("Origin", urlString);
//            connection.setRequestProperty("Priority", "u=1, i");
//            connection.setRequestProperty("Referer", urlString);
//            connection.setRequestProperty("Sec-Fetch-Dest", "empty");
//            connection.setRequestProperty("Sec-Fetch-Mode", "cors");
//            connection.setRequestProperty("Sec-Fetch-Site", "cross-site");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
            // 设置单次请求是否支持重定向，默认为 setFollowRedirects 方法设置的值
            connection.setInstanceFollowRedirects(false);
            connection.setRequestMethod("GET"); // 使用HEAD方法进行请求，不获取内容，仅获得响应头信息
            connection.setConnectTimeout(2000); // 设置连接超时时间
            connection.setReadTimeout(2000); // 设置读取超时时间
            int responseCode = connection.getResponseCode();
            System.out.println(responseCode);
            // 判断响应码是否表示成功（200-399 范围内）
            // 判断响应码是否表示成功（200-399 范围内）
            if (200 <= responseCode && responseCode <= 399) {
                util.setAccessStatus(String.valueOf(responseCode));
                util.setAccess("可访问");
            } else {
                util.setAccessStatus(String.valueOf(responseCode));
                util.setAccess("不可访问");
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 发生异常时，URL不可访问
            if (e instanceof  SSLHandshakeException) {
                if (b) {
                    send(util,!a,false);
                }
            } else {
                util.setAccessStatus("访问异常");
                util.setAccess("不可访问");
            }
        }
    }

    private void send(Map<Integer,String> util,List<Integer> index,boolean a,boolean b) {
        for (Integer i : index) {
            send(util,i,a,b);
        }
    }
    private void send(Map<Integer,String> util,Integer index,boolean a,boolean b) {
        String string = util.get(index);
        String urlString = "";
        if (string.startsWith("https://www.") ) {
            string = string.replace("https://www.","");
        }  else  if (string.startsWith("http://www.") ) {
            string = string.replace("http://www.","");
        }
        if (string.startsWith("www.") ) {
            string = string.replace("www.","");
        }
        urlString = a ? "https://www."+string : "http://www."+string;
        if (StringUtils.isEmpty(string)) {
            log.info("落地页链接为空,跳过!");
            return;
        }
        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
            // 设置单次请求是否支持重定向，默认为 setFollowRedirects 方法设置的值
            connection.setInstanceFollowRedirects(false);
            connection.setRequestMethod("GET"); // 使用HEAD方法进行请求，不获取内容，仅获得响应头信息
            connection.setConnectTimeout(2000); // 设置连接超时时间
            connection.setReadTimeout(2000); // 设置读取超时时间
            int responseCode = connection.getResponseCode();
            System.out.println(responseCode);
            // 判断响应码是否表示成功（200-399 范围内）
            // 判断响应码是否表示成功（200-399 范围内）
            if (200 <= responseCode && responseCode <= 399) {
                util.put(util.size(),String.valueOf(responseCode));
                util.put(util.size(),"可访问");
            } else {
                util.put(util.size(),String.valueOf(responseCode));
                util.put(util.size(),"不可访问");
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 发生异常时，URL不可访问
            if (e instanceof  SSLHandshakeException) {
                if (b) {
                    send(util,index,!a,false);
                }
            } else {
                util.put(util.size(),"访问异常");
                util.put(util.size(),"不可访问");
            }
        }

    }

    public static void main(String[] args) throws Exception {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));

        URL url = new URL("https://textilek.com/products/led3-2");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestProperty("authority", "https://app.upsellingtech.com/");
        connection.setRequestProperty("method", "OPTIONS");
        connection.setRequestProperty("path", "/api/customers");
        connection.setRequestProperty("scheme", "https");
        connection.setRequestProperty("Accept", "*/*");
        connection.setRequestProperty("Accept-Encoding", "gzip, deflate, br, zstd");
        connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9");
        connection.setRequestProperty("Access-Control-Request-Headers", "content-type");
        connection.setRequestProperty("Access-Control-Request-Method", "POST");
        connection.setRequestProperty("Origin", "");
        connection.setRequestProperty("Priority", "u=1, i");
        connection.setRequestProperty("Referer", "");
        connection.setRequestProperty("Sec-Fetch-Dest", "empty");
        connection.setRequestProperty("Sec-Fetch-Mode", "cors");
        connection.setRequestProperty("Sec-Fetch-Site", "cross-site");
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");

        // 设置单次请求是否支持重定向，默认为 setFollowRedirects 方法设置的值
        connection.setInstanceFollowRedirects(true);
        connection.setRequestMethod("HEAD"); // 使用HEAD方法进行请求，不获取内容，仅获得响应头信息
        connection.setConnectTimeout(2000); // 设置连接超时时间
        connection.setReadTimeout(2000); // 设置读取超时时间
        int responseCode = connection.getResponseCode();
        System.out.println(responseCode);
    }
}
